package string;

import edu.princeton.cs.algs4.StdOut;

/**
 * @description: 定义标准字母表
 * @title: Alphabet
 * @author: zp
 * @date: 2022/2/15 20:39
 */
public class Alphabet {
    /*
     * 定义标准字母表
     * */

    //The binary alphabet { 0, 1 }.  01字符集
    public static final Alphabet BINARY = new Alphabet("01");
    //The octal alphabet { 0, 1, 2, 3, 4, 5, 6, 7 }.  八进制字符集
    public static final Alphabet OCTAL = new Alphabet("01234567");
    //The decimal alphabet { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }.  小数集
    public static final Alphabet DECIMAL = new Alphabet("0123456789");
    //The hexadecimal alphabet { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F }.  十六进制字符集
    public static final Alphabet HEXADECIMAL = new Alphabet("0123456789ABCDEF");
    //The DNA alphabet { A, C, T, G }.  DNA字符集
    public static final Alphabet DNA = new Alphabet("ACGT");
    //The lowercase alphabet { a, b, c, ..., z }.  小写字母字符集
    public static final Alphabet LOWERCASE = new Alphabet("abcdefghijklmnopqrstuvwxyz");
    //The uppercase alphabet { A, B, C, ..., Z }.  大写字母字符集
    public static final Alphabet UPPERCASE = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
    //The protein alphabet { A, C, D, E, F, G, H, I, K, L, M, N, P, Q, R, S, T, V, W, Y }.  蛋白质字符集
    public static final Alphabet PROTEIN = new Alphabet("ACDEFGHIKLMNPQRSTVWY");
    //The base-64 alphabet (64 characters).  base-64字符集
    public static final Alphabet BASE64 = new Alphabet("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
    //The ASCII alphabet (0-127).  ASCII码字符集
    public static final Alphabet ASCII = new Alphabet(128);
    //The extended ASCII alphabet (0-255).  扩展ASCII码字符集
    public static final Alphabet EXTENDED_ASCII = new Alphabet(256);
    //The Unicode 16 alphabet (0-65,535).  Unicode16字符集
    public static final Alphabet UNICODE16      = new Alphabet(65536);


    private char[] alphabet;   //用字符数组存储字母表
    private int[] inverse;   //存储字符的索引值的整数数组(利用字符获得字符的索引值)
    private int R;   //字母表大小

    //构造函数(利用字符串)
    public Alphabet(String alpha){
        boolean[] unicode=new boolean[Character.MAX_VALUE];

        for(int i=0;i<alpha.length();i++){
            char c=alpha.charAt(i);
            //避免重复字符
            if(unicode[c]){
                throw new IllegalArgumentException("Illegal alphabet: repeated character = '" + c + "'");
            }
            unicode[c]=true;
        }
        alphabet=alpha.toCharArray();  //将字符串转换为字符型数组并用作字母表
        R=alpha.length();
        inverse=new int[Character.MAX_VALUE];

        //将索引值各项初始化为-1，然后再单独修改使用到的字符
        //这样返回值为-1就表示该字符不在字母表中
        for(int i=0;i<inverse.length;i++){
            inverse[i]=-1;
        }
        for(int i=0;i<R;i++){
            inverse[alphabet[i]]=i;
        }
    }

    //构造函数(利用整数)
    public Alphabet(int radix){
        this.R=radix;
        alphabet=new char[R];
        inverse=new int[R];
        for(int i=0;i<R;i++){
            alphabet[i]=(char)i;
        }
        for(int i=0;i<R;i++){
            inverse[i]=i;
        }
    }

    //默认构造
    public Alphabet(){this(256);}

    //字符c是否存在字母表中
    public boolean contains(char c){ return inverse[c]!=-1; }

    //字母表大小
    public int R(){return R;}

    //一个索引所需的比特数
    public int lgR(){
        int lgR=0;
        for(int t=R-1;t>=1;t/=2){
            lgR++;
        }
        return lgR;
    }

    //获取字符的索引
    public int toIndex(char c){
        if(c>=inverse.length||inverse[c]==-1){
            throw new IllegalArgumentException("Character " + c + " not in alphabet");
        }
        return inverse[c];
    }

    //将s转换为R进制整数
    public int[] toIndices(String s){
        char[] source=s.toCharArray();
        int[] target=new int[s.length()];
        for(int i=0;i<s.length();i++){
            target[i]=toIndex(source[i]);
        }
        return target;
    }

    //将R进制整数转换为字符
    public char toChar(int index){
        if(index>=R||index<0){
            throw new IllegalArgumentException("index must be between 0 and " + R + ": " + index);
        }
        return alphabet[index];
    }

    //将R进制整数数组转换为字符串
    public String toChars(int[] indices){
        StringBuilder s=new StringBuilder(indices.length);
        for(int i=0;i<indices.length;i++){
            s.append(toChar(indices[i]));
        }
        return s.toString();
    }

    public static void main(String[] args) {
        int[]  encoded1 = Alphabet.BASE64.toIndices("NowIsTheTimeForAllGoodMen");
        String decoded1 = Alphabet.BASE64.toChars(encoded1);
        StdOut.println(decoded1);

        int[]  encoded2 = Alphabet.DNA.toIndices("AACGAACGGTTTACCCCG");
        String decoded2 = Alphabet.DNA.toChars(encoded2);
        StdOut.println(decoded2);

        int[]  encoded3 = Alphabet.DECIMAL.toIndices("01234567890123456789");
        String decoded3 = Alphabet.DECIMAL.toChars(encoded3);
        StdOut.println(decoded3);
    }

}
